<?php

class Procedure
{
  
  private $profile;
  
  private $participants;
  
  private $tents;
  
  private $default_chapter;
  
  private $first_time;
  
  private $user_id;
  
  private $password;
  
  private $booking;
  
  /**
   * @var sfLogger $logger
   */
  private $logger;

  public function __construct (sfGuardUser $user = null, sfLogger $logger)
  {
//    /$this->logger = $logger;
    
    if (is_null($user) || $user->isNew())
    {
      $this->first_time = true;
      $this->profile = new sfGuardUserProfile();
      $this->participants = array();
      $this->tents = array();
    }
    else
    {
      $this->first_time = false;
      $this->profile = $user->getProfile();
      $this->participants = array();
//      $participants_collection = $user->getBooking()->getParticipants();
      $participants_collection = ParticipantTable::getInstance()->findByBookingId($user->getBooking()->getId());
      foreach($participants_collection as $participant) {
        $this->participants[$participant->getId()] = $participant->toArray(false);
      }
      $this->tents = array();
      $tents_collection = $user->getBooking()->getTents();
      foreach($tents_collection as $tent) {
        $this->tents[$tent->getId()] = $tent->toArray(false);
      }
      sfContext::getInstance()->getLogger()->log(sprintf("Number of tents for user %s retrieved from database upon initalizing procedure: %s", $this->profile->getUser()->getUsername(), count($this->tents)));
    }
  }
  
  /**
   * @return sfLogger
   */
  public function getLogger() {
    //return $this->logger;
    return sfContext::getInstance()->getLogger();
  }
  
  /**
   * @param sfGuardUser $user
   */
  public function setUser (sfGuardUser $user=null) {
    $this->__construct($user);
  }

  /**
   * @return sfGuardUserProfile
   */
  public function getUserProfile ()
  {
    return $this->profile;
  }

  /**
   * @return array
   */
  public function getParticipants ()
  {
    return $this->participants;
  }
  
  /**
   * @return Doctrine_Collection
   */
  public function getParticipantsCollection()
  {
    $this->getLogger()->debug(sprintf('Calling %s', __METHOD__));
    $participants = $this->getParticipants();
    $coll = new Doctrine_Collection('Participant');
    foreach($participants as $i => $participant) {
      $id = $participant['id'];
      $this->getLogger()->debug(sprintf('Participant id %s', $id));
      if($id > 0) {
        $participant_object = ParticipantTable::getInstance()->findOneById($id);
      } 
      if(!($id > 0) || $participant_object == null) {
        $this->getLogger()->debug('Intializing new participant instance');
        $participant_object = new Participant();
      }
      $this->getLogger()->debug(sprintf('Price scaling id before assigning values from procedure: %s', $participant_object->getPriceScalingId()));
      foreach($participant as $k => $v) {
        if(($k == 'price_scaling_id' && !($participant_object->getPriceScalingId() > 0)) || 
        $k != 'price_scaling_id')  
          $participant_object->set($k, $v);
      }
      $this->getLogger()->debug(sprintf('Price scaling id after assigning values from procedure: %s', $participant_object->getPriceScalingId()));
      $coll->add($participant_object);
    }
    return $coll;
  }

  /**
   * @return array
   */
  public function getTents ()
  {
    return $this->tents;
  }
  public function getTentsCollection()
  {
    $tents = $this->getTents();
    $coll = new Doctrine_Collection('Tent');
    foreach($tents as $i => $tent) {
      $id = $tent['id'];
      if($id > 0) {
        $tent_object = TentTable::getInstance()->findOneById($id);
      } 
      if(!($id > 0) || $tent_object == null) {
        $tent_object = new Tent();
      }
      foreach($tent as $k => $v) {
        $tent_object->set($k, $v);
      }
      $coll->add($tent_object);
    }
    return $coll;
  }

  public function updateFromAboutMeForm (AboutMeForm $form)
  {
    if (! $form->isBound())
    {
      throw new InvalidArgumentException('Form must be bound.');
    }
    
    $this->profile = $form->getObject();
    $user_participant = new Participant();
    $user_participant->setFirstName($this->profile->getFirstName());
    $user_participant->setLastName($this->profile->getLastName());
    
    $exists = false;
    foreach ($this->participants as $i => $participant)
    {
      if ($participant->getFirstName() == $this->profile->getFirstName() && $participant->getLastName() == $this->profile->getLastName())
      {
        $user_participant = $participant;
        // TODO Is this save enough, determining the participant of the user by names only?
        $exists = $i;
        break;
      }
    
    }
    if ($form->getValue('is_participant') && $exists === false)
    {
      if($user_participant->getId() > 0) {
        $this->participants[$user_participant->getId()] = $user_participant;
      } else {
        $this->participants[] = $user_participant;
      }
      //$this->participants->add($user_participant);
    }
    elseif (! $form->getValue('is_participant') && $exists !== false)
    {
      if($user_participant->getId() > 0) {
        unset($this->participants[$user_participant->getId()]);
      } else {
        // TODO
      }
      //$this->participants->remove($exists);
    }
  }

  public function updateFromDynamicParticipantForm (DynamicParticipantForm $form)
  {
    $this->getLogger()->debug(sprintf('Calling %s', __METHOD__));
    if (! $form->isBound())
    {
      throw new InvalidArgumentException('Form must be bound.');
    }
    
    $price_scaling = PriceScalingTable::getInstance()->getCurrent();
    $this->getLogger()->debug(sprintf('Applying price scaling %s', $price_scaling));
    $this->participants = $form->getParticipants();
    $this->getLogger()->debug(sprintf('%s participants returned by form', count($this->participants)));
    foreach($this->participants as $i => $participant) {
        $this->getLogger()->debug(sprintf('Values of participant %s: %s', $i, print_r($participant, true)));
        if(!$participant['id']) {
          $participant['price_scaling_id'] = $price_scaling->getId();
          $this->getLogger()->debug(sprintf('Assigning price scaling %s to participant %s', $price_scaling, $i));
        }
        $this->participants[$i] = $participant;
    }
  }

  public function updateFromDynamicTentForm (DynamicTentForm $form)
  {
    if (! $form->isBound())
    {
      throw new InvalidArgumentException('Form must be bound.');
    }
    
    $this->tents = $form->getTents();
  }

  public function getParticipantDefaultValues ()
  {
    return array(
      'chapter' => $this->profile->getChapter());
  }

  public function save ($conn, sfLogger $logger, sfGuardSecurityUser $session_user)
  {
    $this->getLogger()->debug(sprintf('Calling %s', __METHOD__));
    $this->getLogger()->debug(sprintf('Is first time %s', ($this->isFirstTime() ? 'TRUE' : 'FALSE')));

    
    $this->profile = $this->refreshRecord($this->profile);
    // Save user
    if($this->isFirstTime()) {
      $pwg = new PasswordGenerator();
      $this->password = $pwg->generate();
      $this->getLogger()->debug(sprintf('Generated new password %s for user %s.', $this->password, $this->profile->getEmailAddress()));
      $user = new sfGuardUser();
      $user->setEmailAddress($this->profile->getEmailAddress());
      $user->setUsername($this->profile->getEmailAddress());
      $user->setPassword($this->password);
      $user->setIsActive(true);
      $user->setIsSuperAdmin(false);
      $user->setProfile($this->profile);
      $user->save($conn);
      $session_user->signIn($user);
      $this->getLogger()->debug(sprintf('Newly created user %s is now signed in.', $this->profile->getEmailAddress()));
    } else {
      $this->getLogger()->debug('Retriving guard user from action');
      $user = $session_user->getGuardUser();
      $this->getLogger()->debug(sprintf('Retrieved user %s', $user));
      $this->getLogger()->debug('Retrieving profile from user');
      $this->profile = $user->getProfile();
      $this->getLogger()->debug(sprintf('Retrieved user profile %s', $this->profile));
    }
    
    $this->getLogger()->debug(sprintf('Dealing with user %s', $user));
    $this->profile->setUser($user);
    $this->profile->save($conn);
    $this->getLogger()->debug('User profile saved');
    
    // Save new booking
    $this->getLogger()->debug('Saving booking...');
    if($this->isFirstTime()) {
      $booking = new Booking();
      $this->getLogger()->debug(sprintf('Dealing with new booking'));
    } else {
      $booking = BookingTable::getInstance()->findOneByUserId($user->getId());
      $this->getLogger()->debug(sprintf('Dealing with retrieved booking %s', $booking));
    }
    $booking->setUser($user);
    $booking->save($conn);
    $this->booking = $booking;
    $this->getLogger()->debug('Booking saved');
    
    // Saving participants
    $this->getLogger()->debug('Saving participants...');
    $current_participants = ParticipantTable::getInstance()->findByBookingId($this->booking->getId());
    $this->getLogger()->debug(print_r($current_participants->getKeys(), true));
    $this->getLogger()->debug(sprintf('There are currently %s participants already assigned to the booking', count($current_participants)));
    $this->getLogger()->debug(sprintf('%s participants are to be stored', count($this->participants)));
    foreach($this->participants as $i => $participant) {
      $id = $participant['id'];
      $this->getLogger()->debug(sprintf('Saving participant with id %s', ($id ? $id : 'NEW')));
      if($id > 0) {
        $participant_object = ParticipantTable::getInstance()->findOneById($id);
        foreach($current_participants as $j => $current_participant) {
          if($current_participant->getId() == $id) {
            $current_participants->remove($j);
            break;
          }
        }
      } else {
        $participant_object = new Participant();
      }
      foreach($participant as $k => $v) {
        if(($k == 'price_scaling_id' && !($participant_object->getPriceScalingId() > 0)) || 
        $k != 'price_scaling_id')  
          $participant_object->set($k, $v);
      }
      
      $participant_object->setBooking($booking);
      $participant_object->save();
      $this->getLogger()->debug('Participant saved');
    }
    
    // Deleting participants
    if(count($current_participants) > 0) {
      $this->getLogger()->debug('Deleting participants...');
      foreach($current_participants as $participant) {
        $this->getLogger()->debug(sprintf('Deleting participant %s', $participant->getId()));
        $participant->delete();
      }
    }
    
    // Saving tents
    $this->getLogger()->debug('Saving tents...');
    $current_tents = TentTable::getInstance()->findByBookingId($this->booking->getId());
    $this->getLogger()->debug(sprintf('There are currently %s tents already assigned to the booking', count($current_tents)));
    $this->getLogger()->debug(sprintf('%s tents are to be stored', count($this->tents)));
    foreach($this->tents as $i => $tent) {
      $id = $tent['id'];
      $this->getLogger()->debug(sprintf('Saving tent with id %s', ($id ? $id : 'NEW')));
      if($id > 0) {
        $tent_object = TentTable::getInstance()->findOneById($id);
        if($current_tents->contains($id)) {
          $this->getLogger()->debug('Tent is already in database');
          $current_tents->remove($id);
        }
      } else {
        $tent_object = new Tent();
      }
      foreach($tent as $k => $v) {
        $tent_object->set($k, $v);
      }
      $tent_object->setBooking($booking);
      $tent_object->save();
      $this->getLogger()->debug('Tent saved');
    }
    
    // Deleting participants
    if(count($current_tents) > 0) {
      $this->getLogger()->debug('Deleting tents...');
      foreach($current_tents as $tent) {
        $this->getLogger()->debug(sprintf('Deleting tent %s', $tent->getId()));
        $tent->delete();
      }
    }
  }
  
  
  /**
   * @return boolean
   */
  public function isFirstTime() {
    return $this->first_time;
  }
  
  private function refreshRecord(sfDoctrineRecord $record) {
    $values = $record->toArray(false);
    $class = get_class($record);
    $record = new $class();
    $record->setArray($values);
    return $record;
  }
  
  public function getBooking() {
    return $this->booking;
  }
  
  public function getPassword() {
    return $this->password;
  }
}